﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;




namespace GoodTrader.GTOrder
{
    public class COrderManager
    {
        private List<COrder> m_OrderList = new List<COrder>();
        private List<CAccount> m_AccountList = new List<CAccount>();

        // 계좌 번호 배열
        private Array m_arAccount = null;
        public Array GetAccountArray()
        {
            return m_arAccount;
        }
        // 체결 정보
        private DSCBO1Lib.CpFConclusionClass m_CpFConclusion = new DSCBO1Lib.CpFConclusionClass();
        public DSCBO1Lib.CpFConclusionClass CpFConclusion
        {
            get { return m_CpFConclusion; }
            set { m_CpFConclusion = value; }
        }
        // 주문 유틸
        private CPTRADELib.CpTdUtilClass m_TdUtil = new CPTRADELib.CpTdUtilClass();
        public CPTRADELib.CpTdUtilClass TdUtil
        {
            get { return m_TdUtil; }
            set { m_TdUtil = value; }
        }
        // 주문 객체 
        private CPTRADELib.CpTd6831Class m_Cp6831 = new CPTRADELib.CpTd6831Class();
        public CPTRADELib.CpTd6831Class Cp6831
        {
            get { return m_Cp6831; }
            set { m_Cp6831 = value; }
        }
        // 수정 객체
        private CPTRADELib.CpTd6831Class m_Cp6832 = new CPTRADELib.CpTd6831Class();
        public CPTRADELib.CpTd6831Class Cp6832
        {
            get { return m_Cp6832; }
            set { m_Cp6832 = value; }
        }
        // 취소 객체
        private CPTRADELib.CpTd6831Class m_Cp6833 = new CPTRADELib.CpTd6831Class();
        public CPTRADELib.CpTd6831Class Cp6833
        {
            get { return m_Cp6833; }
            set { m_Cp6833 = value; }
        }
        // 종목 코드 객체
        private CPUTILLib.CpFutureCode m_FCode = new CPUTILLib.CpFutureCode();
        // 당일 매매 손익 조회 객체
        private CPTRADELib.CpTd6197Class m_Cp6197 = new CPTRADELib.CpTd6197Class();
        public CPTRADELib.CpTd6197Class Cp6197
        {
            get { return m_Cp6197; }
            set { m_Cp6197 = value; }
        }

        // 당일 매매 손익 조회 객체
        private CPTRADELib.CpTd0723Class m_CpTd0723 = new CPTRADELib.CpTd0723Class();
        public CPTRADELib.CpTd0723Class CpTd0723
        {
            get { return m_CpTd0723; }
            set { m_CpTd0723 = value; }
        }
        // 주문 초기화 변수 - 이 변수가 참일 때만 주문이 진행된다.
        private bool m_OrderInitialized = false;
        public bool OrderInitialized
        {
            get { return m_OrderInitialized; }
            set { m_OrderInitialized = value; }
        }

        // 주문을 위해 초기화 한다.
        public void InitOrder()
        {
            if (m_OrderInitialized == true)
                return;

            //주문 초기화
            int nRet;
            do
            {
                nRet = m_TdUtil.TradeInit(0);
            } while (nRet != 0);

            //주문이벤트등록
            m_CpFConclusion.Received += new DSCBO1Lib._IDibEvents_ReceivedEventHandler(OnCpConcluded);
            m_CpFConclusion.Subscribe();

            //ReadAccountInfo();
            //계좌번호 얻기                        
            m_arAccount = (Array)m_TdUtil.AccountNumber;
            for (int i = 0; i < m_arAccount.GetLength(0); i++)
            {
                // 계좌번호로 테이블을 만든다.
                string accountNumber = m_arAccount.GetValue(i).ToString();
                CAccount newAccount = new CAccount(accountNumber);
                m_AccountList.Add(newAccount);
            }
            OrderInitialized = true;
        }

        public void RequestOrder(COrderParameter a_OrderParameter, CAccount a_Account)
        {
            if (a_OrderParameter == null || a_Account == null)
                return;
            int curTime = GTGlobal.TimeToInt();
            // 자동 주문이 설정되어 있고 주문시작시간과 종료 시간 내에서만 주문이 나간다.
            if (GTGlobal.AutoOrder == true && 
                curTime >= GTGlobal.OrderStartTime &&
                curTime <= GTGlobal.OrderEndTime)
            {
                SendOrder(a_OrderParameter, a_Account);
            }
        }

        private void SendOrder(COrderParameter a_OrderParameter, CAccount a_Account)
        {
            if (a_OrderParameter == null || a_Account == null)
                return;

            CPTRADELib.CpTd6831Class orderObject = new CPTRADELib.CpTd6831Class();

            orderObject.SetInputValue(1, a_OrderParameter.AccountNumber);    // 계좌번호
            orderObject.SetInputValue(2, a_OrderParameter.Code);   // 종목코드
            orderObject.SetInputValue(3, a_OrderParameter.Amount);     // 주문수량
            orderObject.SetInputValue(4, a_OrderParameter.Price); // 주문 가격 - 시장가인 경우는 0으로 설정한다.
            orderObject.SetInputValue(5, a_OrderParameter.OrderType);    // 매수 혹은 매수
            orderObject.SetInputValue(6, a_OrderParameter.OrderMode);  // 시장가 혹은 지정가 등
            orderObject.SetInputValue(7, a_OrderParameter.OrderCond);  // 주문 조건 코드 0:없음, 1:IOC, 2:FOK

            int nRet = 0;
            try
            {
                // 데이터 수신 요청을 한다.
                nRet = orderObject.BlockRequest();
            }
            catch (System.Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            /*
             * 리턴값
            0: 정상요청
            1: 통신요청 실패
            2: 주문확인창에서 취소
            3: 그외의 내부 오류
            4: 주문요청 제한 개수 초과
            */
            // 반환값이 0이 아니면 오류가 발생한 것이다.
            if (nRet != 0)
            {
                return;
            }

            // 주문확인 후에 처리함수를 부른다.
            OnOrderConfirmed(orderObject, a_OrderParameter, a_Account);
        }

        /*
         * @brief : 서버에서 주문을 확인했을 때 처리하는 함수. 여기서 주문 객체를 생성하여 주문 목록에 집어 넣는다.
         * @parameter a_OrderParamter : 최초 주문 정보
         * @remark : 여기까지 왔다면 주문이 이미 서버에 전달된 상태이다. 
         *           만약 여기까지 오지 않으면 주문 요청을 했지만 주문이 되지 않은 상태이다.
         *           그러므로 주문요청이 어떤 식으로든 처리가 되지 않았을 경우에 대한 대비가 필요하다. 
         *           재주문이나 주문취소 등으로 대처하는 것이 좋다. 
         */
        private void OnOrderConfirmed(CPTRADELib.CpTd6831Class a_OrderObject, COrderParameter a_OrderParameter, CAccount a_Account)
        {
            if (a_OrderObject == null || a_OrderParameter == null || a_Account == null)
                return;

            COrder newOrder = new COrder();
            // 계좌객체
            newOrder.Account = a_Account;
            // 주문번호
            newOrder.Number = a_OrderObject.GetHeaderValue(8).ToString();
            // 종목코드
            newOrder.Code = a_OrderObject.GetHeaderValue(13).ToString();
            // 종목 코드가 다르면 주문을 진행하지 않는다.
            if (newOrder.Code != a_OrderParameter.Code)
                return;
            // 계좌 번호
            newOrder.AccountNumber = a_OrderObject.GetHeaderValue(11).ToString();
            if (newOrder.AccountNumber != a_OrderParameter.AccountNumber)
                return;
            // 주문이 발생한 봉의 일일 인덱스
            newOrder.DayIndex = a_OrderParameter.DayIndex;
            // 주문이 요청된 시간
            newOrder.RequestedTime = a_OrderParameter.RequestedTime;
            // 주문 수량
            newOrder.RequestedAmount = a_OrderParameter.Amount;
            // 주문 가격
            newOrder.RequestedPrice = a_OrderParameter.Price;
            // 주문 확정 시간
            newOrder.ConfirmedTime = GTGlobal.TimeToInt();
            // 주문 타입 : 매수, 매도
            if (a_OrderParameter.OrderType == "2")
                newOrder.OrderType = GTOrderType.BUY;
            else if (a_OrderParameter.OrderType == "1")
                newOrder.OrderType = GTOrderType.SELL;
            else
                newOrder.OrderType = GTOrderType.NONE;

            // 주문 모드 : 지정가, 시장가, 조건부지정가, 최유리가
            if (a_OrderParameter.OrderMode == "1")
                newOrder.OrderMode = GTOrderMode.AT_PRICE;
            else if (a_OrderParameter.OrderMode == "2")
                newOrder.OrderMode = GTOrderMode.AT_MARKET;
            else if (a_OrderParameter.OrderMode == "3")
                newOrder.OrderMode = GTOrderMode.AT_CONDPRICE;
            else if (a_OrderParameter.OrderMode == "4")
                newOrder.OrderMode = GTOrderMode.AT_BESTPRICE;
            else
                newOrder.OrderMode = GTOrderMode.AT_PRICE;
            
            // 주문 조건
            newOrder.OrderCond = a_OrderParameter.OrderCond;
            // 주문확정 상태 대입
            newOrder.State = GTOrderState.CONFIRMED;
            // 주문 목록에 추가한다.
            m_OrderList.Add(newOrder);
        }

        public void OnCpConcluded()
        {
            // 계좌번호를 가져온다.
            string accountNumber = m_CpFConclusion.GetHeaderValue(7).ToString();
            // 주문번호를 가져온다.
            string orderNumber = m_CpFConclusion.GetHeaderValue(5).ToString();
            // 계좌번호와 주문번호로 주문객체를 찾는다.
            COrder foundOrder = FindOrder(accountNumber, orderNumber);
            if (foundOrder != null)
            {
                foundOrder.OriginalOrderNumber = m_CpFConclusion.GetHeaderValue(6).ToString();    // 원주문번호
                foundOrder.ConcludedAmount = Convert.ToInt32(m_CpFConclusion.GetHeaderValue(3));      // 체결수량
                foundOrder.ConcludedPrice = Convert.ToDouble(m_CpFConclusion.GetHeaderValue(4));      // 체결가격
                foundOrder.ConcludedTime = GTGlobal.TimeToIntEx();                 // 체결시각
                foundOrder.Account.RemainCode = m_CpFConclusion.GetHeaderValue(45).ToString(); // 잔고구분코드
                foundOrder.Account.Remain = Convert.ToInt32(m_CpFConclusion.GetHeaderValue(46));         // 잔고
                foundOrder.Account.LiqAmount = Convert.ToInt32(m_CpFConclusion.GetHeaderValue(47)); // 청산가능수량

                // 주문상태에 따라 다르게 처리한다.
                int orderState = Convert.ToInt32(m_CpFConclusion.GetHeaderValue(44));
                if (orderState == 1)
                    OnApplied(foundOrder, orderNumber);
                else if (orderState == 2)
                    OnModified(foundOrder, orderNumber);
                else if (orderState == 3)
                    OnCancelled(foundOrder, orderNumber);
                else if (orderState == 4)
                    OnConcluded(foundOrder, orderNumber);
                else if (orderState == 5)
                    OnDenied(foundOrder, orderNumber);
                else
                    OnError(foundOrder, orderNumber);
            }
        }
        /*
         * @brief : 주문객체를 찾아낸다.
         * @param a_AccountNumber : 계좌 번호
         * @param a_OrderNumber : 주문번호
         */ 
        private COrder FindOrder(string a_AccountNumber, string a_OrderNumber)
        {
            for(int i = 0; i < m_OrderList.Count; i++)
            {
                COrder curOrder = m_OrderList[i];
                if (curOrder.AccountNumber == a_AccountNumber &&
                    curOrder.Number == a_OrderNumber)
                {
                    return curOrder;
                }
            }

            return null;
        }

        // 주문 접수 되었을 때 처리
        private void OnApplied(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.APPLIED;
        }

        // 주문이 수정되었을 때 처리
        private void OnModified(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.MODIFIED;
        }

        // 주문이 취소되었을 때 처리
        private void OnCancelled(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.CANCELED;
        }

        // 주문이 체결되었을 때 처리
        private void OnConcluded(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.CONCLUDED;
            
        }

        // 주문이 거부되었을 때 처리
        private void OnDenied(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.DENIED;
        }

        // 주문에 대하 알수없는 오류가 발생했을 때 처리
        private void OnError(COrder a_Order, string a_OrderNumber)
        {
            a_Order.State = GTOrderState.UNKNOWN;
        }
    }
}
